#include <type_traits>
#include "module.hh"
#include "service.hh"
#include "../corelib/logger/writer.hh"

namespace kratos { namespace engine {

Module::Module()
    : handle_(INVALID_MODULE_HANDLE), service_(nullptr), type_(ModuleType::NONE) {
}

Module::Module(Module && rht) {
    handle_ = rht.handle_;
    service_ = rht.service_;
    type_ = rht.type_;
    rht.handle_ = INVALID_MODULE_HANDLE;
    rht.service_ = nullptr;
}

Module::~Module() {
    if (handle_ != INVALID_MODULE_HANDLE) {
        unload();
    }
    handle_ = INVALID_MODULE_HANDLE;
    service_ = nullptr;
}

bool Module::load(const std::string& name) {
#	ifdef WIN32
    handle_ = ::LoadLibraryA(name.c_str());
#	else
    handle_ = dlopen(name.c_str(), RTLD_NOW);
#	endif // WIN32
    auto result = (handle_ != INVALID_MODULE_HANDLE);
    if (result) {
        name_ = name;
    }
    return result;
}

bool Module::unload() {
    if (handle_ == INVALID_MODULE_HANDLE) {
        logerror << "Module's handle is invalid" << corelib::logend;
        return false;
    }
    if (service_) {
        logerror << "Module was installed" << corelib::logend;
        return false;
    }
#   ifdef WIN32
    auto func = (UninstallExportFunc)::GetProcAddress(handle_, UninstallExportFuncName);
#   else
    auto func = (UninstallExportFunc)dlsym(handle_, UninstallExportFuncName);
#   endif
    if (func) {
        func();
    } else {
        logerror << "Module's uninstall export not found, module name:" << name_ << corelib::logend;
    }
#	ifdef WIN32
    if (FALSE == ::FreeLibrary(handle_)) {
        logerror << "FreeLibrary, Windows system errno:" << corelib::info::syserrno <<
            ", error:" << corelib::info::syserror << corelib::logend;
        if (FALSE == ::UnmapViewOfFile(handle_)) {
            logerror << "UnmapViewOfFile, Windows system errno:" << corelib::info::syserrno <<
                ", error:" << corelib::info::syserror << corelib::logend;
        }
    }
#	else
    dlclose(handle_);
#	endif // WIN32
    handle_ = INVALID_MODULE_HANDLE;
    service_ = nullptr;
    return true;
}

bool Module::stop() {
    if (handle_ == INVALID_MODULE_HANDLE) {
        return false;
    }
    if (service_) {
        service_->stop();
    }
    return true;
}

bool Module::uninstall() {
    if (handle_ == INVALID_MODULE_HANDLE) {
        return false;
    }
    if (service_) {
        service_->uninstall();
    }
    service_ = nullptr;
    return true;
}

Service* Module::getService() {
    if (handle_ == INVALID_MODULE_HANDLE) {
        return nullptr;
    }
    if (service_) {
        return service_;
    }
#   ifdef WIN32
    auto func = (ModuleExportFunc)::GetProcAddress(handle_, ModuleExportFuncName);
#   else
    auto func = (ModuleExportFunc)dlsym(handle_, ModuleExportFuncName);
#   endif
    if (!func) {
        return nullptr;
    }
    service_ = func();
    return service_;
}

Module::operator bool() {
    return (handle_ != INVALID_MODULE_HANDLE && (nullptr != getService()));
}

ModuleType Module::getModuleType() {
    if (ModuleType::NONE == type_) {
#       ifdef WIN32
        auto func = (ModuleTypeExportFunc)::GetProcAddress(handle_, ModuleTypeExportFuncName);
#       else
        auto func = (ModuleTypeExportFunc)dlsym(handle_, ModuleTypeExportFuncName);
#       endif
        if (func) {
            type_ = func();
        } else {
            if (getService()) {
                type_ = static_cast<ModuleType>(getService()->getType());
            }
        }
    }
    return type_;
}

void Module::clearService() {
    service_ = nullptr;
}

void Module::forceUnload() {
    if (handle_ == INVALID_MODULE_HANDLE) {
        logerror << "Module's handle is invalid" << corelib::logend;
    }
#	ifdef WIN32
    if (FALSE == ::FreeLibrary(handle_)) {
        logerror << "FreeLibrary, Windows system errno:" << corelib::info::syserrno <<
            ", error:" << corelib::info::syserror << corelib::logend;
        if (FALSE == ::UnmapViewOfFile(handle_)) {
            logerror << "UnmapViewOfFile, Windows system errno:" << corelib::info::syserrno <<
                ", error:" << corelib::info::syserror << corelib::logend;
        }
    }
#	else
    dlclose(handle_);
#	endif // WIN32
    handle_ = INVALID_MODULE_HANDLE;
}

void Module::setServiceName(const std::string& serviceName) {
    auto service = getService();
    if (service) {
        service->setServiceName(serviceName);
    }
}

}}
